<template>
    <div class="dialog">
        <div class="title">
            <span>{{ user.nickname }}</span>
            <div class="action-menu">
                <VPopover trigger="click" popStyle="padding-top: 10px; z-index: 1000;">
                    <template #reference>
                        <div class="more">
                            <i class="iconfont icon-gengduo"></i>
                        </div>
                    </template>
                    <template #content>
                    </template>
                </VPopover>
            </div>
        </div>
        <!-- 聊天记录组件 -->
        <MessageList :mid="mid"></MessageList>
        <!-- 发送框 -->
        <div class="send-box">
            <div class="row">
                <div class="space-margin emoji" ref="emoji">
                    <svg t="1702369380130" @click="emojiBoxShow = !emojiBoxShow" class="icon" title="表情"
                        viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4229"
                        id="mx_n_1702369380130" width="24" height="24">
                        <path
                            d="M512 989.866667C249.173333 989.866667 34.133333 776.533333 34.133333 512 34.133333 249.173333 249.173333 34.133333 512 34.133333s477.866667 215.04 477.866667 477.866667c0 264.533333-215.04 477.866667-477.866667 477.866667z m0-904.533334C276.48 85.333333 85.333333 276.48 85.333333 512s191.146667 426.666667 426.666667 426.666667 426.666667-191.146667 426.666667-426.666667S747.52 85.333333 512 85.333333z"
                            p-id="4230"></path>
                        <path
                            d="M512 783.75822187c-136.53333333 0-191.14666667-107.40622187-192.96711147-111.0471104-3.64088853-9.10222187-3.64088853-18.2044448 1.8204448-27.30666667 5.46133333-7.28177813 14.5635552-12.74311147 23.66577814-12.74311146h338.60266666c9.10222187 0 18.2044448 5.46133333 23.66577707 12.74311146 5.46133333 7.28177813 5.46133333 18.2044448 1.8204448 27.30666667-5.46133333 3.64088853-60.07466667 111.04711147-196.608 111.0471104z m-112.8675552-96.4835552c23.66577813 20.02488853 60.07466667 41.87022187 112.8675552 41.87022187 52.79288853 0 89.20177813-21.84533333 112.8675552-41.87022187H399.1324448zM704.96711147 494.3075552c-7.28177813 0-14.5635552-1.8204448-20.0248896-7.28177706l-72.81777707-72.81777814c-10.92266667-10.92266667-10.92266667-27.30666667 0-38.22933333l72.81777707-72.81777813c10.92266667-10.92266667 27.30666667-10.92266667 38.22933333 0 10.92266667 10.92266667 10.92266667 27.30666667 0 38.22933333l-52.79288853 52.7928896 52.79288853 52.79288853c10.92266667 10.92266667 10.92266667 27.30666667 0 38.22933334-3.64088853 7.28177813-10.92266667 9.10222187-18.20444373 9.10222186zM319.03288853 494.3075552c-7.28177813 0-14.5635552-1.8204448-20.02488853-7.28177706-10.92266667-10.92266667-10.92266667-27.30666667 0-38.22933334l52.79288853-52.7928896-52.79288853-52.79288853c-10.92266667-10.92266667-10.92266667-27.30666667 0-38.22933333 10.92266667-10.92266667 27.30666667-10.92266667 38.22933333 0l72.81777814 72.81777813c10.92266667 10.92266667 10.92266667 27.30666667 0 38.22933333l-72.81777814 72.81777707c-3.64088853 3.64088853-10.92266667 5.46133333-18.2044448 5.46133333z"
                            p-id="4231"></path>
                    </svg>
                    <!-- 表情选择组件 -->
                    <EmojiBox v-model:show="emojiBoxShow" @insertEmoji="insertEmoji"></EmojiBox>
                </div>
            </div>
            <!-- 自制输入框 可识别转换表情  -->
            <!-- 不建议复制，要的话一定要纯文本，否则会有奇怪的标签 -->
            <div class="input-box">
                <div ref="editor" class="core-style" contenteditable="true" @input="handleInput"
                    @compositionstart="isComposite = true" @compositionend="compositionend"
                    @click="(e) => handleEditorClick(e)" @keydown="handleKeyDown" @paste="handlePaste"
                    style="height: 60px;"></div>
                <div class="indicator">
                    <span :class="{ 'too-much': input.length > 500 }">{{ input.length }}</span>/<span>500</span>
                </div>
            </div>
            <div class="row right">
                <button class="btn-box send-btn" @click="sendMsg" :title="`enter 发送\nshift + enter 换行`">发送</button>
            </div>
        </div>
    </div>
</template>

<script setup>
import { ref, onMounted, onBeforeUnmount, watch } from 'vue';
import { useUserStore } from '@/store/user';
import VPopover from '@/components/popover/Popover.vue';
import EmojiBox from '@/components/emoji/EmojiBox.vue';
import MessageList from '@/components/message/MessageList.vue';
import EmojiJson from '@/assets/json/emoji.json';
import { ElMessage } from 'element-plus';
import { updateWhisperOnline, updateWhisperOutline } from '@/api/chat.js';
import { useRoute, useRouter } from 'vue-router';

// 获取 store 实例
const store = useUserStore();

const route = useRoute();
const router = useRouter();

// 定义所有的响应式变量
const baseUrl = window.location.origin;
const mid = ref(0);  // 当前聊天用户的 UID
const user = ref({
    uid: null,
    nickname: null
});
const myId = ref(null);  // 用于存储自己的 UID 以便发送离线消息
const emojiBoxShow = ref(false);  // 控制表情框是否显示
const input = ref("");  // 保存输入框内容
const emojiMap = ref(EmojiJson);  // 表情列表
const isComposite = ref(false);  // 是否正在输入中文
const rangeOfEditor = ref(null);  // 存储光标位置

const editor = ref(null);
const emoji = ref(null);

// 辅助函数：转义正则表达式中的特殊字符
function escapeRegExp(string) {
    return string.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&');
}

// 请求相关方法
const updateUser = () => {
    // 在聊天列表中查找当前聊天对象并更新用户信息
    let i = store.chatList.findIndex(item => item.user.uid === mid.value);
    user.value = store.chatList[i].user;
    myId.value = store.chatList[i].chat.anotherId;
};

const updateOnline = async () => {
    // 更新当前聊天窗口的在线状态
    await updateWhisperOnline(user.value.uid);
};

const updateOutline = async () => {
    // 更新当前聊天窗口的离线状态
    await updateWhisperOutline(user.value.uid, myId.value);
};

const sendMsg = () => {
    // 判断是否给自己发消息，若是则提示错误
    if (user.value.uid === store.user.uid) {
        ElMessage.error("不能给自己发消息哦~");
        return;
    }
    // 判断消息长度是否超过限制
    if (input.value.length > 500) {
        ElMessage.error("字数超过限制");
        return;
    }
    // 判断是否有内容输入
    if (input.value.length === 0) {
        ElMessage.error("随便说点吧");
        return;
    }
    // 判断是否连接到 WebSocket 服务
    if (!store.ws) {
        ElMessage.error("服务已断开，请刷新后尝试");
        return;
    }

    // 构造消息对象并发送
    const msg = {
        code: 101,
        anotherId: user.value.uid,
        content: input.value,
    };
    store.ws.send(JSON.stringify(msg));
    // 清空输入框
    editor.value.innerHTML = '';
    input.value = "";  
};

const init = () => {
    editor.value.innerHTML = '';
    input.value = "";
    rangeOfEditor.value = null;
}

const handleOutsideClick = (event) => {
    if (!emoji.value.contains(event.target)) {
        emojiBoxShow.value = false;
    }
};
// 表情相关方法
const handleInput = () => {
    // 如果正在输入中文拼音，则不进行表情替换
    if (isComposite.value) return;

    const editorContent = editor.value.innerHTML;
    let updatedContent = editorContent.toString();

    // 替换带有图片的HTML为表情代号
    for (var i = 0; i < emojiMap.value.length; i++) {
        const emojiCode = emojiMap.value[i].name;
        const emojiUrl = emojiMap.value[i].url;
        const emojiImgTag = `<img src="${emojiUrl}" alt="${emojiCode}" title="${emojiCode}" class="msg-input_emoji" contenteditable="false" draggable="false">`;
        const emojiImgTagCopy = `<img src="${baseUrl}${emojiUrl}" alt="${emojiCode}" title="${emojiCode}" class="msg-input_emoji" contenteditable="false" draggable="false" style="font-family: &quot;Harmony Font&quot;; font-size: 14px;">`;
        const emojiTmgTagCopy2 = `<img src="${baseUrl}${emojiUrl}" alt="${emojiCode}" title="${emojiCode}" class="msg-input_emoji" contenteditable="false" draggable="false" style="background-color: transparent; font-size: 14px; font-family: &quot;Harmony Font&quot;;">`;
        const emojiTmgTagCopy3 = `<img src="${emojiUrl}" alt="${emojiCode}" title="${emojiCode}" class="msg-input_emoji" contenteditable="false" draggable="false" style="background-color: transparent; font-size: 14px; font-family: &quot;Harmony Font&quot;;">`;
        // 全部替换
        updatedContent = updatedContent.replace(new RegExp(escapeRegExp(emojiImgTag), 'g'), emojiCode);
        updatedContent = updatedContent.replace(new RegExp(escapeRegExp(emojiImgTagCopy), 'g'), emojiCode);
        updatedContent = updatedContent.replace(new RegExp(escapeRegExp(emojiTmgTagCopy2), 'g'), emojiCode);
        updatedContent = updatedContent.replace(new RegExp(escapeRegExp(emojiTmgTagCopy3), 'g'), emojiCode);
    }

    // 替换隐藏的<span>标签 复制的内容可能会有携带 (不建议复制，要的话一定要纯文本，否则会有奇怪的标签)
    const spanStart = '<span style="background-color: transparent;">';
    const spanEnd = '</span>'
    const br = '<br>'
    const divStart = '<div>';
    const divEnd = '</div>';
    updatedContent = updatedContent.replace(new RegExp(escapeRegExp(spanStart), 'g'), '');
    updatedContent = updatedContent.replace(new RegExp(escapeRegExp(spanEnd), 'g'), '');
    updatedContent = updatedContent.replace(new RegExp(escapeRegExp(br), 'g'), '');
    updatedContent = updatedContent.replace(new RegExp(escapeRegExp(divStart), 'g'), '');
    updatedContent = updatedContent.replace(new RegExp(escapeRegExp(divEnd), 'g'), '');

    // 更新格式化后的 input 数据
    input.value = updatedContent;
    // console.log(input.value);
};


// 在光标位置插入表情
const insertEmoji = (emoji) => {
    const img = document.createElement('img');
    img.src = emoji.url;
    img.alt = emoji.name;
    img.title = emoji.name;
    img.classList.add("msg-input_emoji");
    img.contentEditable = false;
    img.draggable = false;

    // 如果从未点过文本框就设置光标位置到文本框
    if (!rangeOfEditor.value) {
        rangeOfEditor.value = new Range();
        rangeOfEditor.value.selectNodeContents(editor.value);
    }
    // 如果是选择模式
    if (!rangeOfEditor.value.collapsed)  {
        if (
            rangeOfEditor.value.startContainer === rangeOfEditor.value.endContainer &&
            rangeOfEditor.value.startContainer.nodeName === "#text" &&
            rangeOfEditor.value.startOffset === 0 && 
            rangeOfEditor.value.endOffset === rangeOfEditor.value.endContainer.length
        ) {
            // 选中同一段文本的全部 删除该段文本元素
            editor.value.removeChild(rangeOfEditor.value.endContainer);
        }
        else if (
            rangeOfEditor.value.startContainer !== rangeOfEditor.value.endContainer &&
            rangeOfEditor.value.startContainer.nodeName === "#text" &&
            rangeOfEditor.value.endContainer.nodeName === "#text" &&
            rangeOfEditor.value.startOffset === 0 && 
            rangeOfEditor.value.endOffset === rangeOfEditor.value.endContainer.length
        ) {
            // 选中不同两段文本之间的全部 删除该两段段文本之间的全部元素
            const childNodesArray = Array.from(editor.value.childNodes);
            const i = childNodesArray.indexOf(rangeOfEditor.value.startContainer);
            const j = childNodesArray.indexOf(rangeOfEditor.value.endContainer);
            for (var k = i; k <= j; k ++) {
                editor.value.removeChild(editor.value.childNodes[i]);
            }                    
        }
        else {
            // 否则只删除选中内容
            rangeOfEditor.value.deleteContents();
        }
    }
    // 此时有可能光标在一个文本元素中
    if (rangeOfEditor.value.endContainer.nodeName === "#text") {
        const childNodesArray = Array.from(editor.value.childNodes);
        const i = childNodesArray.indexOf(rangeOfEditor.value.endContainer);

        if (rangeOfEditor.value.endContainer.length ===rangeOfEditor.value.endOffset) {
            // 光标在文本末尾 设置光标到编辑器下文本域后边
            let range = new Range();
            range.selectNodeContents(editor.value);
            range.setStart(editor.value, i + 1);
            range.setEnd(editor.value, i + 1);
            range.collapse(true);
            rangeOfEditor.value = range;
        } else if (rangeOfEditor.value.endOffset === 0) {
            // 光标在文本开头 设置光标到编辑器下文本域前边
            let range = new Range();
            range.selectNodeContents(editor.value);
            range.setStart(editor.value, i);
            range.setEnd(editor.value, i);
            range.collapse(true);
            rangeOfEditor.value = range;
        }
    }
    // 光标位置设置好后在这插入图标
    rangeOfEditor.value.insertNode(img);
    // 但是true会出bug
    rangeOfEditor.value.collapse(false);
    // 将焦点设置回输入框
    editor.value.focus();
    document.getSelection().removeAllRanges();
    document.getSelection().addRange(rangeOfEditor.value);
    // 触发input事件
    editor.value.dispatchEvent(new Event('input', { bubbles: true }));
};

// 点击编辑框中的表情，定位光标到表情前
const handleEditorClick = (event) => {
    let target = event.target;
    if (target.tagName.toLowerCase() === "img") {
        let range = new Range();
        range.setStartBefore(target);
        range.collapse(true);
        document.getSelection().removeAllRanges();
        document.getSelection().addRange(range);
    }
};

// 处理粘贴事件
const handlePaste = async (event) => {
    event.preventDefault();
    try {
        const pastedText = await navigator.clipboard.readText();
        insertTextAtCursor(pastedText);
    } catch (error) {
        console.error('读取剪贴板数据失败:', error);
    }
};

// 在光标位置插入文本
const insertTextAtCursor = (text) => {
    const selection = window.getSelection();
    const range = selection.getRangeAt(0);
    const textNode = document.createTextNode(text);
    range.deleteContents();  // 删除选中的内容
    range.insertNode(textNode);  // 插入新的文本节点
    range.setStartAfter(textNode);  // 设置光标位置到文本后
    range.setEndAfter(textNode);
    selection.removeAllRanges();
    selection.addRange(range);  // 更新选择范围
};

// 键盘按下的回调函数
const handleKeyDown = (event) => {
    const isShiftPressed = event.shiftKey;
    if (event.key === 'Enter' && !isShiftPressed) {
        event.preventDefault();
        sendMsg();  // 按下回车键发送消息
    } else if (event.key === 'Backspace') {
        // 判断是否删除表情
        const rangeAt = document.getSelection().getRangeAt(0);
        const startContainer = rangeAt.startContainer;
        const endContainer = rangeAt.endContainer;
        if (startContainer.nodeName === "DIV" &&
            endContainer.nodeName === "DIV" &&
            rangeAt.collapsed &&
            rangeAt.startOffset > 0 &&
            editor.value.childNodes[rangeAt.startOffset - 1].nodeName === "IMG"
        ) {
            event.preventDefault();
            editor.value.removeChild(editor.value.childNodes[rangeAt.startOffset - 1]);  // 删除前面的表情
            editor.value.dispatchEvent(new Event('input', { bubbles: true }));  // 触发输入事件
        }
    } else if (event.key === "Delete") {
        // 判断是否删除后面的表情
        const rangeAt = document.getSelection().getRangeAt(0);
        const startContainer = rangeAt.startContainer;
        const endContainer = rangeAt.endContainer;
        if (startContainer.nodeName === "DIV" &&
            endContainer.nodeName === "DIV" &&
            rangeAt.collapsed &&
            rangeAt.startOffset < editor.value.childNodes.length &&
            editor.value.childNodes[rangeAt.startOffset].nodeName === "IMG"
        ) {
            event.preventDefault();
            editor.value.removeChild(editor.value.childNodes[rangeAt.startOffset]);  // 删除后面的表情
            editor.value.dispatchEvent(new Event('input', { bubbles: true }));  // 触发输入事件
        }
    }
};

// 处理中文输入结束事件
const compositionend = () => {
    isComposite.value = false;
    handleInput();  // 重新处理输入
};

// 监听选区变化，更新光标位置
const selectionChange = () => {
    let selection = document.getSelection();
    if (selection.rangeCount > 0) {
        const range = selection.getRangeAt(0);
        if (editor.value.contains(range.commonAncestorContainer)) {
            rangeOfEditor.value = range;
        }
    }
};

// 初始化并挂载时的逻辑
onMounted(async () => {
    mid.value = Number(route.params.mid);
    updateUser();
    await updateOnline();
    window.addEventListener("click", handleOutsideClick);
    document.addEventListener("selectionchange", selectionChange);
    window.addEventListener('beforeunload', updateOutline);  // 监听标签页关闭事件
});

// 清理事件监听
onBeforeUnmount(() => {
    window.removeEventListener("click", handleOutsideClick);
    document.removeEventListener("selectionchange", selectionChange);
    window.removeEventListener('beforeunload', updateOutline);
});

// 监听路由变化，切换聊天窗口
watch(() => route.path, async () => {
    await updateOutline();
    mid.value = Number(route.params.mid);
    if (!route.params.mid) return;
    init();
    updateUser();
    await updateOnline();
});
</script>

<style scoped>
.dialog {
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex;
    -webkit-box-orient: vertical;
    -webkit-box-direction: normal;
    -ms-flex-direction: column;
    flex-direction: column;
    -webkit-box-align: stretch;
    -ms-flex-align: stretch;
    align-items: stretch;
    height: 100%;
}

.title {
    height: 36px;
    -ms-flex-negative: 0;
    flex-shrink: 0;
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex;
    -webkit-box-pack: center;
    -ms-flex-pack: center;
    justify-content: center;
    -webkit-box-align: center;
    -ms-flex-align: center;
    align-items: center;
    border-bottom: 1px solid #e9eaec;
    position: relative;
    text-align: center;
    color: #333;
}

.title span {
    font-size: 14px;
}

.action-menu {
    position: absolute;
    top: 6px;
    right: 6px;
    z-index: 2;
}

.more {
    width: 25px;
    height: 25px;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    color: #999;
}

.send-box {
    height: 162px;
    border-top: 1px solid #d8d8d8;
    border-bottom-right-radius: 4px;
    background-color: #f4f5f7;
    -ms-flex-negative: 0;
    flex-shrink: 0;
    padding: 0 16px;
    position: relative;
    z-index: 2;
}

.row {
    height: 48px;
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex;
    -webkit-box-orient: horizontal;
    -webkit-box-direction: normal;
    -ms-flex-direction: row;
    flex-direction: row;
    -webkit-box-align: center;
    -ms-flex-align: center;
    align-items: center;
}

.space-margin {
    margin-right: 16px;
}

.emoji {
    position: relative;
}

.emoji .icon {
    fill: #999;
    cursor: pointer;
}

.emoji .icon:hover {
    fill: var(--brand_pink);
}

.input-box {
    position: relative;
}

.core-style {
    width: 100%;
    min-height: 22px;
    max-height: 300px;
    letter-spacing: 1px;
    -webkit-box-sizing: border-box;
    box-sizing: border-box;
    white-space: pre-wrap;
    overflow-x: hidden;
    overflow-y: auto;
    color: #222;
    background-color: transparent;
    font-size: 14px;
    vertical-align: baseline;
    outline: none;
    line-height: normal;
    border-radius: 4px;
    word-break: break-word;
    word-wrap: break-word;
}

.core-style:hover,
.core-style:focus,
.core-style:active {
    background: transparent;
}

.core-style::-webkit-scrollbar {
    width: 8px;
    height: 8px;
    background-color: rgba(1, 1, 1, 0);
}

.core-style::-webkit-scrollbar-thumb {
    background: #aaa;
    border-radius: 4px;
}

.indicator {
    color: #c0c0c0;
    position: absolute;
    bottom: -32px;
    right: 100px;
    padding: 1px;
    font-size: 12px;
}

.too-much {
    color: #f00;
}

.row {
    height: 48px;
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex;
    -webkit-box-orient: horizontal;
    -webkit-box-direction: normal;
    -ms-flex-direction: row;
    flex-direction: row;
    -webkit-box-align: center;
    -ms-flex-align: center;
    align-items: center;
}

.row.right {
    -webkit-box-pack: end;
    -ms-flex-pack: end;
    justify-content: flex-end;
    height: 46px;
}

.btn-box {
    outline: none;
    cursor: pointer;
    margin: 0;
    position: relative;
    width: 88px;
    height: 30px;
    text-align: center;
    border: 1px solid #e7e7e7;
    font-size: 14px;
    line-height: 14px;
}

.send-btn {
    background-color: #fff;
    border-radius: 4px;
    color: #555;
}
</style>